package server;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.*;
import java.util.regex.Matcher;

public class Bootstrap {

    //定义socket端口
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    Mapper mapper = new Mapper();

    ServerSocket[] sockets = new ServerSocket[0];

    /**
     * Minicat启动需要初始化的一些操作
     */
    public void start() throws Exception {

        //加载解析相关的配置，web.xml
        //loadServlet();

        //加载解析相关的配置，server.xml
        loadServerXml();

        //加载appBase下的项目
        loadWebApp();

        /**
         * 1.0
         * 需要浏览器请求:http://localhost:8080 ，返回一个固定字符串页面"Hello MiniCat"
         */
//        ServerSocket serverSocket = new ServerSocket(port);

//        System.out.println("==========> MiniCat start on port: " + port);
/*        while (true){
            Socket socket = serverSocket.accept();
            //有了socket，接收请求，获取输出流
            OutputStream outputStream = socket.getOutputStream();
            String data = "Hello MiniCat";
            String responseText = HttpProtocolUtil.getHttpHeader200(data.length()) + data;
            outputStream.write(responseText.getBytes());
            socket.close();
        }*/

        /**
         * 2.0
         * 封装Request和Response对象，返回html静态资源文件
         */
/*        while (true){
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream();
            Request request = new Request(inputStream);
            Response response = new Response(outputStream);
            response.outputHtml(request.getUrl());
            socket.close();
        }*/

        /**
         * 3.0
         * 可以请求动态资源(Servlet)
         */
/*        while (true){
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream();
            Request request = new Request(inputStream);
            Response response = new Response(outputStream);

            if(servletMap.get(request.getUrl()) == null){
                response.outputHtml(request.getUrl());
            }else{
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request, response);
            }

            socket.close();
        }*/

        /**
         * 多线程改造(不使用线程池)
         */
/*
        while (true){
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, servletMap);
            requestProcessor.start();
        }
*/


        for (ServerSocket serverSocket : sockets) {
            System.out.println("==========> MiniCat start on port: " + serverSocket.getLocalPort());
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    int corePoolSize = 10;
                    int maximumPoolSize = 100;
                    long keepAliveTime = 100L;
                    TimeUnit unit = TimeUnit.SECONDS;
                    BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(100);
                    ThreadFactory threadFactory = Executors.defaultThreadFactory();
                    RejectedExecutionHandler abortPolicy = new ThreadPoolExecutor.AbortPolicy();
                    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,
                            maximumPoolSize,keepAliveTime,unit,queue,threadFactory,abortPolicy);
                    while (true){
                        Socket socket = null;
                        try {
                            socket = serverSocket.accept();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        RequestProcessor requestProcessor = new RequestProcessor(socket, mapper.hostList);
                        threadPoolExecutor.execute(requestProcessor);
                    }
                }
            });
            thread.start();
        }
    }

    //查找文件绝对路径
    private List<String> doScanPath(String scanPath, String type, List<String> pathList) {
        File pack = new File(scanPath);
        File[] files = pack.listFiles();
        for (File file : files) {
            if(file.isDirectory()){
                //递归
                doScanPath(scanPath + File.separator + file.getName(), type, pathList);
            }else {
                if (file.getName().endsWith(type)) {
                    String path = file.getPath();
                    path.replaceAll("/", Matcher.quoteReplacement(File.separator)).replaceAll("\\\\", Matcher.quoteReplacement(File.separator));
                    System.out.println(path);
                    pathList.add(path);
                }
            }
        }
        return pathList;
    }

    //加载App
    private void loadWebApp() throws Exception {
        for (Mapper.Host host : mapper.hostList) {
            File file = new File(host.getAppBase());
            ClassLoadExpand classLoadExpand = new ClassLoadExpand();
            List<Mapper.Context> contextList = new ArrayList<>();
            if(file.exists() && file.isDirectory()) {
                File[] subFile = file.listFiles();
                for (File file1 : subFile) {
                    if(file1.isDirectory()){
                        String path = file1.getPath();
                        String webName = path.substring(path.lastIndexOf(File.separator)+1);
                        System.out.println(webName);
                        //查找web.xml
                        List<String> strings = doScanPath(host.getAppBase() + File.separator + webName, ".xml", new ArrayList<String>());
                        Optional<String> first = strings.stream().filter(e -> e.indexOf("web.xml") != -1).findFirst();
                        List<Mapper.Wrapper> wrapperList = null;
                        if(first.isPresent()) {
                            classLoadExpand.setLocation(host.getAppBase() + File.separator + webName);
                            wrapperList = loadServlet(first.get(), classLoadExpand);
                        }else {
                            wrapperList = new ArrayList<>();
                        }

                        Mapper.Wrapper[] wrappers = new Mapper.Wrapper[wrapperList.size()];
                        wrapperList.toArray(wrappers);
                        Mapper.Context context = new Mapper.Context(webName, wrappers);

                        //加载静态资源html等
                        List<String> htmlPath = doScanPath(host.getAppBase() + File.separator + webName, ".html", new ArrayList<String>());
                        context.staticFilePath = htmlPath;
                        contextList.add(context);
                    }
                }
            }

            Mapper.Context[] contexts = new Mapper.Context[contextList.size()];
            contextList.toArray(contexts);
            host.setContexts(contexts);
        }
    }

    //加载server.xml
    private void loadServerXml() throws Exception {
        InputStream resourceAsStream = Bootstrap.class.getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(resourceAsStream);
        Element rootElement = document.getRootElement();
        List<Element> selectNodes = rootElement.selectNodes("//Service");
        sockets = new ServerSocket[selectNodes.size()];

        for (int i = 0; i < selectNodes.size(); i++) {
            List<Element> connectors = selectNodes.get(i).selectNodes("//Connector");
            for (Element connector : connectors) {
                Attribute port = connector.attribute("port");
                String portStr = port.getStringValue();
                sockets[i] = new ServerSocket(Integer.parseInt(portStr));
            }

            Element engine = (Element)selectNodes.get(i).selectSingleNode("//Engine");
            List<Element> hosts = engine.selectNodes("//Host");
            for (Element host : hosts) {
                Attribute name = host.attribute("name");
                Attribute appBase = host.attribute("appBase");
                String nameStr = name.getStringValue();
                String appBaseStr = appBase.getStringValue().replaceAll("\\\\", "/");
                Mapper.Host hostObj = new Mapper.Host(nameStr, appBaseStr, new Mapper.Context[0]);
                mapper.hostList.add(hostObj);
            }
        }
    }

//    private Map<String, HttpServlet> servletMap = new HashMap<>();

    //加载web.xml
    private List<Mapper.Wrapper> loadServlet(String webXmlPath, ClassLoadExpand classLoadExpand) throws Exception {
//        InputStream resourceAsStream = Bootstrap.class.getClassLoader().getResourceAsStream(webXmlPath);
        FileInputStream resourceAsStream  = new FileInputStream(webXmlPath);
        List<Mapper.Wrapper> wrapperList = new ArrayList<>();

        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(resourceAsStream);
        Element rootElement = document.getRootElement();
        List<Element> list = rootElement.selectNodes("//servlet");
        for (Element element : list) {
            Element servletNameElement = (Element)element.selectSingleNode("//servlet-name");
            String servletNameValue = servletNameElement.getStringValue();
            Element servletClassElement = (Element)element.selectSingleNode("//servlet-class");
            String servletClassValue = servletClassElement.getStringValue();
            Class<?> aClass = classLoadExpand.findClass(servletClassValue);

            Element servletMappingElement = (Element)rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletNameValue + "']");
            String url = servletMappingElement.selectSingleNode("//url-pattern").getStringValue();
//            servletMap.put(url, (HttpServlet) Class.forName(servletClassValue).newInstance());

            Mapper.Wrapper wrapper = new Mapper.Wrapper(url, (HttpServlet)aClass.newInstance());
            wrapperList.add(wrapper);
        }

        return wrapperList;
    }


    /**
     * MiniCat启动入口
     * @param args
     */
    public static void main(String[] args) throws Exception {
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
